/*
MAXCLIENT-ben definiált maximális számú klienssel összeköttetés létesítése
A szerver egy 5 betűs szavakból álló láncolt listát üzemeltet. Ezt kezdetben töltsük fel 2 elemmel.
Ha egy klienstől “SET” parancs után (attól space-szel elválasztva) egy 5 betűs szót kap,
azt beteszi a láncolt listába.
Más üzenet esetén – kivéve a „START“ parancsot – „NEM ERTEM“ választ küld vissza az üzenetet küldő kliensnek.
Ha bármely klienstől “START” parancs érkezik, a szerver “KEZDODIK” üzenetet küld az összes kliensének,
a láncolt lista hosszát figyelembe véve véletlenszerűen kiválaszt egy 5 betűs szót, és a játék kezdetét veszi.
A játék során a kapcsolódó kliensek egymás után kapnak lehetőséget adatküldésre.
Ha valaki soron kívül küld üzenetet, „NEM TE JOSSZ“ választ kap.
A kliensektől érkező üznetek várt formátuma: „az 5 betűs szóban valamely betűpozíció, betű“ pl. „2 e“.
Feltesszük, hogy a kliensek ilyen formátumban üzennek, itt a hibára nem kell figyelni.
Minden tippről minden kliens értesül.
Ha egy kliens eltalálta, hogy az adott pozícióban milyen betű áll, újra ő,
ellenkező esetben a következő kliens következik.
Ha egymás után mind az 5 – különböző – tippje helyes egy kliensnek, őt hirdeti ki nyertesnek a szerver,
s a játék véget ér.
Ha valaki a játék kezdete után akar csatlakozni, a szerver nem engedi, s „A játék már elkezdődött“ üzenettel elküldi.
Példa egy játék menetére (ha 3 kliens van START érkezésekor):
A szerver által választott szó: ablak
1. kliens tippje: „1 e“
2. kliens tippje: „1 a“, „3 k”
3. kliens tippje: “2 b”, “1 a”, “4 a”, “5 h”
1. kliens tippje: “4 a”, “1 a”, “2 b”, “3 l”, “4 k”     -  ennek hatására ő lett a nyertes
+1 jegy:
A játék folyamán “?” parancs hatására küldje el a szerver a játék eddigi összes tippjét annak helyességével együtt.
Ugyanazokat a (pozíció, betű) tippeket csak egyszer feltüntetve.

+ 1 jegy:
A szerver a tippek mellett küldje ki azt is, milyen IP-ről és portról érkezett a tipp.

+ 1 jegy:
Az alap játék módosuljon úgy, hogy ha egy játék már kezdetét vette,
az újonnan csatlakozó klienseket ne küldje el a szerver, hanem fork-kal kezdjen nekik új játékot.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <netdb.h>
#endif

#define PORT "2222"
#define MAXC 2
#define SZOHOSSZ 3

struct kliens{
	int fd;
	struct kliens* next;
	char ip[200];
	char port[200];
};
struct kliens* kliensek=NULL;
int kliensszam=0;

fd_set master;
int fdmax;
int listensocket;

struct szo{
	int id;
	char s[SZOHOSSZ+1];
	int talalat[SZOHOSSZ];
	struct szo* next;
};

struct szo* szavak=NULL;
szavakszama=0;

int game=0;
struct szo* celszo;
struct kliens* sorosjatekos;


void szohozzafuz(char betuk[SZOHOSSZ+1]){
	struct szo* s=(struct szo*)malloc(sizeof(struct szo));
	memset(s->s,'\0',SZOHOSSZ+1);
	strncpy(s->s,betuk,SZOHOSSZ);
	s->next=szavak;
	s->id=szavakszama;
	szavak=s;
	szavakszama++;
}

void felvesz(int ujfd,struct sockaddr sa){
	struct kliens* kl;
	int p;

	if(game){
		elutasit(ujfd,"a jetek mar elkezdodott\n");
		return;
	}
	kl=(struct kliens*)malloc(sizeof(struct kliens));
	kl->fd=ujfd;
	

#ifdef _WIN32
	sprintf(kl->ip,"%s",inet_ntoa( ((struct sockaddr_in*)&sa)->sin_addr) );
#else
	inet_ntop(sa.sa_family, &(((struct sockaddr_in*) &sa)->sin_addr), kl->ip,
			INET6_ADDRSTRLEN);
#endif

	p=ntohs(((struct sockaddr_in*)&sa)->sin_port);
	sprintf(kl->port,"%d",p);

	kl->next=kliensek;
	kliensek=kl;
	kliensszam++;
	FD_SET(ujfd,&master);
	if (ujfd>fdmax) {
		fdmax=ujfd;
	}
}

void szavaklista(int i){
	char uz[200];
	struct szo* s=szavak;
	while(s!=NULL){
		memset(uz,'\0',200);
		strcat(uz,s->s);
		strcat(uz,"\n");
		send(i,uz,strlen(uz),0);
		s=s->next;
	}
}

int elutasit(int ujfd, char c[200]){
	char uz[200];
	memset(uz,'\0',200);
	strcpy(uz,c);
	send(ujfd,uz,strlen(uz),0);
#ifdef _WIN32
	closesocket(ujfd);
#else
	close(ujfd);
#endif
}

void nemertem(int i){
	char uz[200];
	memset(uz,'\0',200);
	strcpy(uz,"nem ertem\n");
	send(i,uz,strlen(uz),0);
}

void msg(int i,char* c){
	char uz[200];
	memset(uz,'\0',200);
	strcpy(uz,c);
	send(i,uz,strlen(uz),0);
}

void torol(int i){
	int j;
	int ujfdmax;
	struct kliens* elozo;
	struct kliens* kl;

	kl=kliensek;
	if (kliensek->fd==i) {
		kl=kliensek;
		kliensek=kliensek->next;
	} else {
		kl=kliensek->next;
		elozo=kliensek;
		while(kl->fd!=i){
			elozo=kl;
			kl=kl->next;
		}
		elozo->next=kl->next;
	}

	kliensszam--;
	FD_CLR(i,&master);

	for (j = 0; j <= fdmax; ++j) {
		if (FD_ISSET(j,&master)) {
				ujfdmax=j;
		}
	}
	fdmax=ujfdmax;
	printf("torolve: %d\n",kl->fd);
	fflush(stdout);
#ifdef _WIN32
	closesocket(kl->fd);
#else
	close(kl->fd);
#endif
	free(kl);
}

void sendallclient(char s[200]){
	char uz[200];
	struct kliens* kl;

	memset(uz,'\0',200);
	kl=kliensek;
	while(kl!=NULL){
		strcpy(uz,s);
		send(kl->fd,uz,strlen(uz),0);
		kl=kl->next;
	}
}


void gamestart(){
	char uz[200];
	struct szo* s=szavak;

	memset(uz,'\0',200);
	game=1;
	sendallclient("KEZDODIK\n");
	
	int cs=rand()%szavakszama;

	while(s->id!=cs){
		s=s->next;
	}
	celszo=s;
	printf("celszo: ");
	printf("%s",celszo->s);
	printf("\n");
	fflush(stdout);
	int j;
	for (j = 0; j < SZOHOSSZ; ++j) {
		*(celszo->talalat+j)=0;
	}

	sorosjatekos=kliensek;
	msg(sorosjatekos->fd,"tippelj!\n");
}
void kovjatekos(int i){
	msg(i,"kovetkezo jatekos jon\n");
	sorosjatekos=sorosjatekos->next;
	if (sorosjatekos==NULL) {
		sorosjatekos=kliensek;
	}
	msg(sorosjatekos->fd,"tippelj!\n");
}

void eredmenyhirdetes(int i){
	sendallclient("vege a jateknak\n");
	struct kliens* kl=kliensek;
	while(kl!=NULL){
		if (kl->fd==i) {
			msg(kl->fd,"Nyertél!\n");
		} else {
			msg(kl->fd,"Vesztettél!\n");
		}
		kl=kl->next;
	}
	game=0;
}

void celszoelkuldes(){
	char uz[200];
	memset(uz,'\0',200);
	strcat(uz,"Célszó: ");
	int j;
	for (j = 0; j < SZOHOSSZ; ++j) {
		if(*(celszo->talalat+j)==1)
		{
			char ch[2];
			ch[1]='\0';
			ch[0]=celszo->s[j];
			//sprintf(ch,"%s",ch);
			strcat(uz,ch);
		}
		else{
			strcat(uz,"_");
		}
	}
	strcat(uz,"\n");
	sendallclient(uz);
}

void vizsgal(int i,int n, char* res){
	struct kliens* kl=kliensek;
	while(kl->fd!=i){
		kl=kl->next;
	}
	if (i!=sorosjatekos->fd) {
		msg(i,"nem te jossz\n");
		return;
	}
	if (n-1>SZOHOSSZ){
		msg(i,"szohosszon belul kell tippelni\n");
		kovjatekos(i);
		return;
	}
	if (celszo->s[n-1]==res[0]) {
		celszo->talalat[n-1]=1;
		celszoelkuldes();
		int j;
		int ok=1;
		for (j = 0; j < SZOHOSSZ; ++j) {
			if (!celszo->talalat[j]) {
				ok=0;
			}
		}
		if (ok) {
			eredmenyhirdetes(i);
			return;
		}
		msg(i,"tippelj!\n");
		return;
	}
	else{
		kovjatekos(i);
	}
}

void uzenetfeldolgoz(int i){
	char buff[200];
	int readb;
	char* delim=" ";
	char* res=NULL;
	int n;

	memset(buff,'\0',200);

	readb=recv(i,buff,sizeof(buff),0);
	if (readb<=0) {
		torol(i);
	} else {
		if(strncmp(buff,"\r\n",2)==0){
			return;
		}
		if(strncmp(buff,"q",1)==0){
			torol(i);
			return;
		}
		if(strncmp(buff,"l",1)==0){
			szavaklista(i);
			return;
		}
		if(strncmp(buff,"s ",2)==0){
			res=NULL;
			res=strtok(buff,delim);
			res=strtok(NULL,delim);
			while(res!=NULL){
				szohozzafuz(res);
				return;
				res=strtok(NULL,delim);
			}
		}
		if(strncmp(buff,"k",1)==0){
			gamestart(i);
			return;
		}
		
		res=NULL;
		res=strtok(buff,delim);
		n=atoi(res);
		if(0<i<(SZOHOSSZ+1)){
			res=strtok(NULL,delim);
			vizsgal(i,n,res);
			return;
		}
		

		nemertem(i);

	}
}

void figyel(){
	struct addrinfo* res;
	struct addrinfo krit;
	int v=1;

	memset(&krit,0,sizeof(struct addrinfo));
	krit.ai_family=AF_INET;
	krit.ai_socktype=SOCK_STREAM;
	krit.ai_flags=AI_PASSIVE;
	getaddrinfo(NULL,PORT,&krit,&res);
	listensocket=socket(res->ai_family,res->ai_socktype,res->ai_protocol);
	
	setsockopt(listensocket,SOL_SOCKET,SO_REUSEADDR,&v,sizeof v);
	if(bind(listensocket,res->ai_addr,res->ai_addrlen)){
		printf("bind error\n");
		fflush(stdout);
		return;
	}
	listen(listensocket,MAXC);
	FD_ZERO(&master);
	FD_SET(listensocket,&master);
	fdmax=listensocket;
}

void ploop(){
	int i;
	struct sockaddr sa;
	socklen_t sl=sizeof(struct sockaddr);
	int ujfd;

	fd_set readfd;

	while(1){
		readfd=master;
		select(fdmax+1,&readfd,NULL,NULL,NULL);
		
		for (i = 0; i <= fdmax; ++i) {
			if (FD_ISSET(i,&readfd)) {
				if (i==listensocket) {
					ujfd=accept(listensocket,&sa,&sl);
					if (kliensszam<MAXC) {
						felvesz(ujfd,sa);
					} else {
						elutasit(ujfd,"maxc\n");
					}
				} else {
					uzenetfeldolgoz(i);
				}
			}
		}
	}
}



int main(int arg, char **argv) {

#ifdef _WIN32
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		printf("WSAStartup failed with error: %d\n", err);
		return 1;
	}
#endif
	srand(time(NULL));
	szohozzafuz("aba");
	szohozzafuz("aca");

	figyel();
	ploop();

#ifdef _WIN32
	WSACleanup();
#endif
	return 0;
}
